package tech.tuister.cninfo.download.api;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import tech.tuister.cninfo.download.util.CninfoAPI;
import tech.tuister.cninfo.download.util.Stocks;
import tech.tuister.cninfo.download.util.Stocks.Stock;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@CninfoAPI
public class AnnouncementQueryAPI {

    static final CloseableHttpClient httpClient;

    static {
        List<Header> headers = new ArrayList<>();
        headers.add(new BasicHeader(HttpHeaders.ACCEPT, "application/json, text/javascript, */*; q=0.01"));
        headers.add(new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "gzip"));
        headers.add(new BasicHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"));
        headers.add(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive"));
        headers.add(new BasicHeader(HttpHeaders.REFERER, "http://www.cninfo.com.cn/"));
        headers.add(new BasicHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.78"));

        httpClient = HttpClients.custom()
                .setDefaultHeaders(headers)
                .build();
    }


    public static final String BASE_DIR = System.getProperty("save.base.dir",
            getEnv("SAVE_BASE_DIR", "D:\\BaiduSyncdisk\\企业财报\\"));

    static final String URL = "http://www.cninfo.com.cn/new/hisAnnouncement/query";

    static String getEnv(String key, String defaultValue) {
        String value = System.getenv(key);

        if (value != null && !value.isEmpty()) {
            return value;
        }

        return defaultValue;
    }

    //下载最近90个定期报告
    public static void download(String stockKey) throws IOException {
        for (int i = 1; i < 4; i++) {
            download(stockKey, i);
        }
    }

    static final String DEFAULT_ANN_CATEGORY = "category_ndbg_szsh;category_bndbg_szsh;category_yjdbg_szsh;category_sjdbg_szsh;";

    public static UrlEncodedFormEntity buildEntity(Stock stock, int pageNum) {
        Map<String, Object> params = new HashMap<>();
        String stockCode = stock.getCode();

        params.put("stock", stockCode + "," + stock.getOrgId());
        params.put("column", "szse");
        params.put("pageNum", pageNum);
        params.put("tabName", "fulltext");
        params.put("pageSize", 30);
        params.put("category", DEFAULT_ANN_CATEGORY);
        params.put("seDate", "");
        params.put("searchkey", "");
        params.put("secid", "");
        params.put("sortName", "");
        params.put("sortType", "");
        params.put("isHLtitle", "true");

        List<BasicNameValuePair> pairs = params.entrySet().stream()
                .map(e -> new BasicNameValuePair(e.getKey(), e.getValue().toString()))
                .collect(Collectors.toList());

        return new UrlEncodedFormEntity(pairs);
    }

    public static void download(String key, int pageNum) throws IOException {
        if (!new File(BASE_DIR).isDirectory()) {
            log.error("保存路径不合法，'{}'不是一个目录", BASE_DIR);
            return;
        }

        Stock stock = Stocks.findStock(key);
        if (stock == null) {
            log.error("没有找到对应的股票！");
            return;
        }
        log.info("即将下载{}最近的定期报告，股票代码：{}", stock.getZwjc(), stock.getCode());

        HttpPost request = new HttpPost(URL);
        request.setEntity(buildEntity(stock, pageNum));
        request.addHeader(new BasicHeader("X-Requested-With", "XMLHttpRequest"));
        String content = httpClient.execute(request, response -> EntityUtils.toString(response.getEntity()));
        log.debug("Response content: {}", content);

        //处理结果
        JSONObject jsonObject = JSON.parseObject(content);
        JSONArray announcements = jsonObject.getJSONArray("announcements");

        log.info("Find {} announcements for {}", announcements.size(), stock);
        String stockSavePath = BASE_DIR + stock.getZwjc();

        if (!new File(stockSavePath).exists()) {
            log.info("mkdir {}: {}", stockSavePath, new File(stockSavePath).mkdirs());
        }

        //开始下载
        int savedCount = 0;
        for (Object announcement : announcements) {
            boolean saved = saveAnnouncement(announcement, stockSavePath, 1);
            if (saved) {
                savedCount++;
            }

        }
        log.info("{} announcements saved, page: {}, total: {}", savedCount, pageNum, announcements.size());

    }

    public static boolean saveAnnouncement(Object announcement, String stockSavePath, int sleepSeconds) throws IOException {

        long start = System.currentTimeMillis();
        log.info("Try save: \n{}", JSON.toJSONString(announcement, JSONWriter.Feature.PrettyFormat));

        String adjunctUrl = ((JSONObject) announcement).getString("adjunctUrl");
        String announcementTitle = ((JSONObject) announcement).getString("announcementTitle");

        //检查该公告是否已经下载过
        Path savePath = Paths.get(stockSavePath + "\\" + announcementTitle + ".pdf");
        if (Files.exists(savePath)) {
            log.info("{} exist, skip", savePath);
            return false;
        }

        try (InputStream stream = new URL("http://static.cninfo.com.cn/" + adjunctUrl).openStream()) {
            Files.copy(stream, savePath);
            long length = savePath.toFile().length();
            log.info("{} saved in {}ms, length: {}", announcementTitle, (System.currentTimeMillis() - start), length);
        }

        try {
            TimeUnit.SECONDS.sleep(sleepSeconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return true;

    }

}
